package com.beerandjesus.sportofkings.handicapper.profile.resolver;

import com.beerandjesus.sportofkings.handicapper.dao.Database;
import com.beerandjesus.sportofkings.handicapper.model.*;
import com.beerandjesus.sportofkings.handicapper.model.brisnet.singlefile.Horse;
import com.beerandjesus.sportofkings.handicapper.model.brisnet.singlefile.HorseRace;
import com.beerandjesus.sportofkings.handicapper.model.brisnet.singlefile.PastPerformance;
import com.beerandjesus.sportofkings.handicapper.model.brisnet.singlefile.Race;
import com.beerandjesus.sportofkings.handicapper.profile.Profile;
import com.beerandjesus.sportofkings.handicapper.profile.ProfileBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

import static com.beerandjesus.sportofkings.handicapper.profile.ProfileType.CRAMER_DROP_IN_CLASS_POSS_JOCKEY_SWITCH;

/**
 * This profile is characterized by a drop in class from the previous race coupled
 * with a switch to a better jockey or another jockey of the same quality, as
 * described by Marc Cramer.
 * Date: Jul 6, 2012
 * Time: 3:46:46 PM
 */
@Component
public class CramerProfileResolver implements ProfileResolver {

    @Autowired
    private Database database;

    public Profile resolve(HorseRace race, Horse horse) {
        // === Must be a Cramer-defined drop in class
        Race r = race.getRace();
        RaceType type = r.getType();
        if (type == null) {
            return null;
        }
        List<PastPerformance> pps = horse.getPastPerformances();
        if (pps == null || pps.size() == 0) {
            return null;
        }
        PastPerformance lastRacePerformance = pps.get(0);
        if (lastRacePerformance == null) {
            return null;
        }
        RaceType lastType = lastRacePerformance.getRaceType();
        if (lastType == null) {
            return null;
        }
        if (!type.isCramerClassDrop(lastType)) {
            return null;
        }
        // === We have a class drop
        Profile prof = ProfileBuilder.analysisProfile(CRAMER_DROP_IN_CLASS_POSS_JOCKEY_SWITCH, 0);
        // === Must also be a jockey switch, either to a higher class jockey or
        // another jockey of the same class
        TrainerJockeyOwner tjo = horse.getTrainerJockeyOwner();
        if (tjo == null) {
            return prof;
        }
        String jock = tjo.getJockeyName();
        String lastJock = lastRacePerformance.getJockeyName();
        if (jock == null || lastJock == null) {
            return prof;
        }
        if (jock.equals(lastJock)) {
            // Can be the same jockey if the jockey won the last race
            String finPos = lastRacePerformance.getFinishPosition();
            int pos = 0;
            try {
                pos = Integer.valueOf(finPos);
            } catch (NumberFormatException e) {
                // Ignore
            }
            if (pos == 1) {
                // === Profile applies becasue the jockey won the last race
                CramerClassDropJockeySwitchProfile p = ProfileBuilder.cramerClassDropJockeySwitchProfile();
                p.setRiderWonBeforeWithHorse(true);
                return p;
            }
            return null;
        }
        // === We have a jockey switch.
        JockeyRating rating = getDatabase().getJockeyRatingByName(jock);
        JockeyRating lastRating = getDatabase().getJockeyRatingByName(lastJock);
        if (rating == null) {
            return prof;
        } else if (lastRating == null && rating.getRating().equals(CramerJockeyRating.LEADING)) {
            // Switch is to a leading jockey. No matter who rode before, profile applies.
            CramerClassDropJockeySwitchProfile cramerProfile = ProfileBuilder.cramerClassDropJockeySwitchProfile();
            prepareTieResolution(race, type, pps, lastType, jock, rating.getRating(), cramerProfile);
            return cramerProfile;
        } else if (lastRating == null) {
            return prof;
        }
        CramerJockeyRating currentJockeyRating = rating.getRating();
        CramerJockeyRating lastJockeyRating = lastRating.getRating();
        // Can be switch within same class, as long as the class is not unfavorable (UNKNOWN or OTHER)
        if (isSwitchWithinUnfavorableClass(currentJockeyRating, lastJockeyRating)) {
            // === Switch is within unfavorable class. Profile does not apply.
            return null;
        }
        if (currentJockeyRating.ordinal() >= lastJockeyRating.ordinal()) {
            // === We have a switch within a favorable class or to a better class
            CramerClassDropJockeySwitchProfile cramerProfile = ProfileBuilder.cramerClassDropJockeySwitchProfile();
            prepareTieResolution(race, type, pps, lastType, jock, currentJockeyRating, cramerProfile);
            return cramerProfile;
        }
        return null;
    }

    private void prepareTieResolution(HorseRace race, RaceType type, List<PastPerformance> pps, RaceType lastType, String jock, CramerJockeyRating currentJockeyRating, CramerClassDropJockeySwitchProfile cramerProfile) {
        if (currentJockeyRating.equals(CramerJockeyRating.LEADING)) {
            cramerProfile.setLeadingJockey(true);
        }
        if (RaceType.isDropFromAllowanceTo40KClaiming(lastType, type, race.getRace().getPurse())) {
            cramerProfile.setDropFromAllowanceTo40KClaiming(true);
        }
        boolean isWinningJockey = false;
        // Check to see if this jockey won a previous race on this horse
        for (PastPerformance past : pps) {
            if (past.getJockeyName() != null && past.getJockeyName().equals(jock)
                    && past.getFinishPosition() != null && past.getFinishPosition().equals("1")) {
                isWinningJockey = true;
                break;
            }
        }
        cramerProfile.setRiderWonBeforeWithHorse(isWinningJockey);
    }

    private boolean isSwitchWithinUnfavorableClass(CramerJockeyRating currentJockeyRating, CramerJockeyRating lastJockeyRating) {
        return (currentJockeyRating.equals(CramerJockeyRating.OTHER) &&
                lastJockeyRating.equals(CramerJockeyRating.OTHER)) ||
                (currentJockeyRating.equals(CramerJockeyRating.UNKNOWN) &&
                        lastJockeyRating.equals(CramerJockeyRating.UNKNOWN));
    }

    public Database getDatabase() {
        return database;
    }

    public void setDatabase(Database database) {
        this.database = database;
    }
}
